package com.data.csv.service;

import cn.hutool.core.io.file.FileReader;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.data.csv.entity.Role;
import com.data.csv.entity.User;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.net.InetAddress;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Properties;

@Service
@Slf4j
public class DataService {

    @Resource
    private UserService userService;
    @Resource
    private RoleService roleService;

    @Value("${csv.environment:dev}")
    private String environment;
    @Value("${csv.host}")
    private String host;
    @Value("${csv.port:22}")
    private Integer port;
    @Value("${csv.username}")
    private String username;
    @Value("${csv.idRsaPath}")
    private String idRsaPath;
    @Value("${csv.localPath}")
    private String localPath;
    @Value("${csv.sftpPath}")
    private String sftpPath;
    @Value("${csv.jsonPath}")
    private String jsonPath;

    public void task() {
        log.info("任务执行开始 {}", LocalDateTime.now());
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        Session session = this.getSession();
        ChannelSftp channelSftp = this.getChannel(session);
        try {
            String date = LocalDate.now().format(dateTimeFormatter);
            String time = LocalTime.now().format(timeFormatter);
            if (ObjectUtils.isNotEmpty(channelSftp)) {
                channelSftp.put(this.createAccountCsv(date, time), sftpPath);
                channelSftp.put(this.createEntitlementCsv(date, time), sftpPath);
                channelSftp.put(this.createRelationship(date, time), sftpPath);
                channelSftp.put(this.createFlag(date), sftpPath);
                channelSftp.disconnect();
                session.disconnect();
                log.info("文件上传成功 {}", LocalDateTime.now());
            }
        } catch (Exception e) {
            log.info("文件上传失败");
        } finally {
            channelSftp.disconnect();
            session.disconnect();
            log.info("关闭 ssh、sftp 连接");
        }
    }


    /**
     * 读取json文件
     */
    private JSONObject readJsonFile() {
        FileReader reader = new FileReader(jsonPath);
        String content = reader.readString();
        if (StringUtils.isNotBlank(content)) {
            JSONObject jsonObject = JSONObject.parseObject(content);
            return jsonObject.getJSONObject("UserEmail");
        }
        return new JSONObject();
    }

    /**
     * 创建账号csv文件🤨
     */
    private String createAccountCsv(String date, String time) {
        JSONObject userEmail = this.readJsonFile();
        // 表格头
        Object[] head = {"HDR", "CPRS", "72223", "app", environment, "run_Sailpoint_File_Extract.sh", date, date, time, "CST"};
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("user_name", "user_status", "is_lock");
        List<User> userList = userService.list(wrapper);
        // 表尾
        Object[] tail = {"TRL", userList.size()};
        String fileName = "72223.idp.dly_app_prod_account.full." + date + ".csv";
        String filePath = localPath + "/" + fileName;
        try {
            String hostname = InetAddress.getLocalHost().getHostName();
            File csvFile = new File(filePath);
            File parent = csvFile.getParentFile();
            if (parent != null && !parent.exists()) {
                parent.mkdirs();
            }
            csvFile.createNewFile();
            BufferedWriter csvWtriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "GB2312"), 1024);
            this.writeRow(head, csvWtriter);
            for (int i = 0, len = userList.size(); i < len; i++) {
                User user = userList.get(i);
                String userStatus;
                if (user.getUserStatus() == 1) {
                    userStatus = "Active ";
                } else {
                    userStatus = "Inactive";
                }
                String isLock = user.getIsLock();
                if (StringUtils.isBlank(isLock)) {
                    isLock = "0";
                }
                String userName = user.getUserName();
                String email = userEmail.getString(userName);
                Object[] data = {"BDR", userName, "Regular", userStatus, isLock, "0", "", email, "", "",
                        "", "", "", "", "", hostname, "CPRS", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
                        "", "", "", "", "", "", "", "", "", "", "", "72223", environment, "app"};
                this.writeRow(data, csvWtriter);
            }
            this.writeRow(tail, csvWtriter);
            csvWtriter.flush();
            csvWtriter.close();
            log.info("用户文件 生成到本地服务器路径: {}", filePath);
            return filePath;
        } catch (Exception e) {
            log.error("用户文件生成异常 {}", e);
        }
        return null;
    }

    /**
     * 创建角色csv文件🤨
     */
    private String createEntitlementCsv(String date, String time) {
        // 表格头
        Object[] head = {"HDR", "CPRS", "72223", "app", environment, "run_Sailpoint_File_Extract.sh", date, date, time, "CST"};
        QueryWrapper<Role> wrapper = new QueryWrapper<>();
        wrapper.select("id", "role_code");
        List<Role> roleList = roleService.list(wrapper);
        // 表尾
        Object[] tail = {"TRL", roleList.size()};
        try {
            String hostname = InetAddress.getLocalHost().getHostName();
            String fileName = "72223.idp.dly_app_prod_entitlement.full." + date + ".csv";
            String filePath = localPath + "/" + fileName;
            File csvFile = new File(filePath);
            File parent = csvFile.getParentFile();
            if (parent != null && !parent.exists()) {
                parent.mkdirs();
            }
            csvFile.createNewFile();
            BufferedWriter csvWtriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "GB2312"), 1024);
            this.writeRow(head, csvWtriter);
            for (int i = 0, len = roleList.size(); i < len; i++) {
                Role role = roleList.get(i);
                Object[] data = {"BDR", "Group", role.getRoleCode(), "", "", role.getRoleCode(), "", "", "", "", "", 0, hostname, "CPRS", 0, "", "", "", "", "", "", "", "", 72223, environment, "app", ""};
                this.writeRow(data, csvWtriter);
            }
            this.writeRow(tail, csvWtriter);
            csvWtriter.flush();
            csvWtriter.close();
            log.info("角色文件 生成到本地服务器路径: {}", filePath);
            return filePath;
        } catch (Exception e) {
            log.error("角色文件生成异常 {}", e);
        }
        return null;
    }

    /**
     * 创建账号角色关联文件🤔
     */
    private String createRelationship(String date, String time) {
        // 表格头
        Object[] head = {"HDR", "CPRS", "72223", "app", environment, "run_Sailpoint_File_Extract.sh", date, date, time, "CST"};
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("user_name", "role_id");
        List<User> userList = userService.list(wrapper);
        int size = 0;
        try {
            String hostname = InetAddress.getLocalHost().getHostName();
            String fileName = "72223.idp.dly_app_prod_acct_ent_relation.full." + date + ".csv";
            String filePath = localPath + "/" + fileName;
            File csvFile = new File(filePath);
            File parent = csvFile.getParentFile();
            if (parent != null && !parent.exists()) {
                parent.mkdirs();
            }
            csvFile.createNewFile();
            BufferedWriter csvWtriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "GB2312"), 1024);
            this.writeRow(head, csvWtriter);
            for (int i = 0, len = userList.size(); i < len; i++) {
                User user = userList.get(i);
                String roleId = user.getRoleId();
                if (StringUtils.isNotBlank(roleId)) {
                    String[] roleIds = roleId.split(",");
                    size += roleIds.length;
                    QueryWrapper<Role> roleQueryWrapper = new QueryWrapper<>();
                    roleQueryWrapper.select("id", "role_code").in("id", roleIds);
                    List<Role> roleList = roleService.list(roleQueryWrapper);
                    for (int j = 0, l = roleList.size(); j < l; j++) {
                        Role role = roleList.get(j);
                        Object[] data = {"BDR", user.getUserName(), role.getRoleCode(), "Group", hostname, "", "", "", "", 72223, environment, "app"};
                        this.writeRow(data, csvWtriter);
                    }
                }
            }
            // 表尾
            Object[] tail = {"TRL", size};
            this.writeRow(tail, csvWtriter);
            csvWtriter.flush();
            csvWtriter.close();
            log.info("用户角色关系文件 生成到本地服务器路径: {}", filePath);
            return filePath;
        } catch (Exception e) {
            log.error("用户角色关系文件生成异常 {}", e);
        }
        return null;
    }

    /**
     * 创建标记文件
     */
    private String createFlag(String date) {
        try {
            String fileName = "72223.idp.dly_flag." + date + ".flg";
            String filePath = localPath + "/" + fileName;
            File csvFile = new File(filePath);
            File parent = csvFile.getParentFile();
            if (parent != null && !parent.exists()) {
                parent.mkdirs();
            }
            csvFile.createNewFile();
            BufferedWriter csvWtriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "GB2312"), 1024);
            csvWtriter.flush();
            csvWtriter.close();
            return filePath;
        } catch (Exception e) {
            log.error("0 byte 标志文件 {}", e);
        }
        return null;
    }

    private void writeRow(Object[] row, BufferedWriter csvWtriter) throws IOException {
        StringBuilder builder = new StringBuilder();
        for (int i = 0, len = row.length; i < len; i++) {
            Object o = row[i];
            builder.append(o);
            if (i < len - 1) {
                builder.append("|");
            }
        }
        csvWtriter.write(builder.toString());
        csvWtriter.newLine();
    }

    /**
     * 创建sftp连接
     */
    private ChannelSftp getChannel(Session session) {
        try {
            // 打开SFTP通道
            Channel channel = session.openChannel("sftp");
            // 建立SFTP通道的连接
            channel.connect();
            log.info("建立SFTP通道的连接");
            return (ChannelSftp) channel;
        } catch (Exception e) {
            log.error("SFTP通道 连接失败 {}", e);
        }
        return null;
    }

    private Session getSession() {
        Session session = null;
        try {
            // 创建JSch对象
            JSch jSch = new JSch();
            // 根据用户名，主机ip，端口获取一个Session对象
            session = jSch.getSession(username, host, port);
            jSch.addIdentity(idRsaPath);
//            session.setPassword("!@#$qwer1234");
            session.setConfig("StrictHostKeyChecking", "no");
            Properties properties = new Properties();
            //主机公钥确认 无口令 SSH 登录（即通过客户端公钥认证），就可以直接连接到远程主机。
            //这是基于 SSH 协议的自动化任务常用的手段
            properties.put("StrictHostKeyChecking", "no");
            // 为Session对象设置properties
            session.setConfig(properties);
//             设置timeout时间
            session.setTimeout(60000);
            // 通过Session建立链接
            session.connect();
            log.info("ssh 连接成功");
            return session;
        } catch (Exception e) {
            session.disconnect();
            log.error("ssh 连接失败 {}", e);
        }
        return null;
    }

}
